const fs = require('fs');
const path = require('path');
const fse = require('fs-extra');
const glob = require('glob');
const JavaScriptObfuscator = require('javascript-obfuscator');

// 配置参数
const config = {
    inputDir: 'resources/scripts',         // 要处理的文件目录
    outputDir: 'out/scripts',              // 输出目录
    jsPattern: '**/*.js',                  // JS文件匹配模式
    ignorePattern: ['**/node_modules/**', '**/*.md'],  // 忽略的目录
    // 中文字符保护配置
    chineseProtection: {
        // enable: true,
        // encryptionKey: Math.floor(Math.random() * 0xff) + 1  // 随机密钥
    },
    // 混淆选项
    obfuscatorOptions: {
        compact: true,  // 启用紧凑输出模式，移除多余空格和换行，减小代码体积
        controlFlowFlattening: true,  // 启用控制流扁平化，打乱代码执行顺序，使逻辑难以追踪
        controlFlowFlatteningThreshold: 0.75,  // 控制流扁平化应用阈值（0-1），0.75表示75%的代码块会被处理
        deadCodeInjection: true,  // 启用死代码注入，添加无实际作用的代码干扰分析
        deadCodeInjectionThreshold: 0.4,  // 死代码注入阈值（0-1），0.4表示注入40%比例的死代码
        debugProtection: true,  // 启用调试保护，防止通过浏览器开发者工具调试代码
        disableConsoleOutput: false,  // 是否禁用console输出（false表示保留console功能）
        identifierNamesGenerator: 'hexadecimal',  // 标识符命名方式：使用十六进制字符串（如0xabc123）
        log: false,  // 是否输出混淆过程的日志信息
        numbersToExpressions: true,  // 将数字转换为表达式（如1000 → 0x3e8 → (512 << 1) - 32）
        renameGlobals: false,  // 是否重命名全局变量（false表示不修改全局变量名，避免冲突）
        selfDefending: true,  // 启用自防御机制，使代码难以被格式化和反混淆
        simplify: true,  // 启用代码简化，移除冗余逻辑
        splitStrings: true,  // 启用字符串分割，将长字符串拆分为多个短字符串
        splitStringsChunkLength: 5,  // 字符串分割的最大长度（5表示每个子串不超过5个字符）
        stringArray: true,  // 启用字符串数组，将代码中所有字符串提取到一个数组中统一管理
        stringArrayCallsTransform: true,  // 转换字符串数组的调用方式，增加读取难度
        stringArrayCallsTransformThreshold: 0.75,  // 字符串数组调用转换的应用阈值
        stringArrayEncoding: ['base64'],  // 字符串数组编码方式（base64编码）
        stringArrayIndexShift: true,  // 启用字符串数组索引偏移，通过计算获取真实索引
        stringArrayRotate: true,  // 启用字符串数组旋转，打乱数组顺序
        stringArrayShuffle: true,  // 启用字符串数组洗牌，随机排序数组元素
        stringArrayWrappersCount: 2,  // 字符串数组包装器数量（2层包装函数）
        stringArrayWrappersChainedCalls: true,  // 启用包装器链式调用（如fn1(fn2(...))）
        stringArrayWrappersType: 'function',  // 包装器类型为函数
        stringArrayThreshold: 0.75,  // 字符串数组应用阈值（0-1）
        transformObjectKeys: true,  // 转换对象的键名，混淆对象属性访问
        unicodeEscapeSequence: false  // 是否使用Unicode转义序列（false表示不转义，避免可读性下降）
    }
};

// 确保输出目录存在
fse.ensureDirSync(config.outputDir);

/**
 * 转义字符串中的特殊字符
 */
function escapeSpecialChars(str) {
    return str
        .replace(/\\/g, '\\\\')
        .replace(/'/g, '\\\'')
        .replace(/"/g, '\\"')
        .replace(/\n/g, '\\n')
        .replace(/\r/g, '\\r')
        .replace(/\t/g, '\\t');
}

/**
 * 中文字符加密处理 - 修复版
 * 解决引号不匹配问题
 */
function encryptChineseCharacters(code, key) {
    if (!config.chineseProtection.enable) return code;

    // 改进的正则表达式，更准确匹配字符串
    const stringRegex = /(["'])(.*?)\1/g;

    return code.replace(stringRegex, (match, quote, content) => {
        // 检查是否包含中文字符
        if (!/[\u4e00-\u9fa5]/.test(content)) {
            return match; // 不含中文则不处理
        }

        try {
            let encrypted = '';
            for (let i = 0; i < content.length; i++) {
                const charCode = content.charCodeAt(i);
                // 只加密中文字符
                if (charCode >= 0x4e00 && charCode <= 0x9fa5) {
                    encrypted += String.fromCharCode(charCode ^ key);
                } else {
                    encrypted += content.charAt(i);
                }
            }

            // 转义特殊字符，防止引号不匹配
            const escapedEncrypted = escapeSpecialChars(encrypted);

            // 使用函数表达式包裹，避免影响外部作用域
            return `(function(s,k){let r='';for(let i=0;i<s.length;i++){let c=s.charCodeAt(i);r+=String.fromCharCode(c^k)}return r})('${escapedEncrypted}', ${key})`;
        } catch (error) {
            console.warn(`⚠️  处理字符串时出错，跳过该字符串: ${content.substring(0, 20)}...`);
            return match; // 出错时返回原始字符串
        }
    });
}

/**
 * 处理单个JS文件
 */
function processJSFile(filePath) {
    try {
        let code = fs.readFileSync(filePath, 'utf8');

        // 步骤1: 对中文字符进行加密处理
        if (config.chineseProtection.enable) {
            code = encryptChineseCharacters(code, config.chineseProtection.encryptionKey);
        }

        // 步骤2: 使用javascript-obfuscator进行深度混淆
        const obfuscationResult = JavaScriptObfuscator.obfuscate(
            code,
            config.obfuscatorOptions
        );

        // 生成输出路径
        const relativePath = path.relative(config.inputDir, filePath);
        const outputPath = path.join(config.outputDir, relativePath);

        // 确保输出目录存在
        fse.ensureDirSync(path.dirname(outputPath));

        // 写入文件
        fs.writeFileSync(outputPath, obfuscationResult.getObfuscatedCode(), 'utf8');
        console.log(`✅ 处理完成: ${filePath} → ${outputPath}`);

    } catch (error) {
        console.error(`❌ 处理失败: ${filePath}`, error.message);
        // 尝试不进行中文加密直接混淆
        try {
            console.log(`🔄 尝试不进行中文加密处理: ${filePath}`);
            const code = fs.readFileSync(filePath, 'utf8');
            const obfuscationResult = JavaScriptObfuscator.obfuscate(
                code,
                config.obfuscatorOptions
            );
            const relativePath = path.relative(config.inputDir, filePath);
            const outputPath = path.join(config.outputDir, relativePath);
            fse.ensureDirSync(path.dirname(outputPath));
            fs.writeFileSync(outputPath, obfuscationResult.getObfuscatedCode(), 'utf8');
            console.log(`✅ 跳过中文加密处理完成: ${filePath}`);
        } catch (secondError) {
            console.error(`❌ 二次处理失败: ${filePath}`, secondError.message);
        }
    }
}

/**
 * 复制非JS文件
 */
function copyNonJSFile(filePath) {
    try {
        const relativePath = path.relative(config.inputDir, filePath);
        const outputPath = path.join(config.outputDir, relativePath);

        if (fse.existsSync(outputPath) && fse.readFileSync(filePath).equals(fse.readFileSync(outputPath))) {
            console.log(`ℹ️  文件未变更，跳过: ${filePath}`);
            return;
        }

        fse.ensureDirSync(path.dirname(outputPath));
        fse.copySync(filePath, outputPath);
        console.log(`📄 文件已复制: ${filePath} → ${outputPath}`);

    } catch (error) {
        console.error(`❌ 文件复制失败: ${filePath}`, error.message);
    }
}

/**
 * 处理所有文件
 */
function processAllFiles() {
    console.log('🚀 开始处理文件...');
    console.log(`🔑 中文加密密钥: ${config.chineseProtection.encryptionKey}`);

    const pattern = path.join(config.inputDir, '**', '*');
    const files = glob.sync(pattern, {
        nodir: true,
        ignore: config.ignorePattern
    });

    if (files.length === 0) {
        console.log('⚠️ 未找到任何文件');
        return;
    }

    console.log(`🔍 找到 ${files.length} 个文件`);

    files.forEach(filePath => {
        if (filePath.endsWith('.js')) {
            processJSFile(filePath);
        } else {
            copyNonJSFile(filePath);
        }
    });

    console.log('🎉 所有文件处理完成！');
}

// 运行脚本
processAllFiles();
